define([
		'angular'
	],
	function (angular) {
		'use strict';

		var module = angular.module('grafana.services');

		// This service was based on OpenJS library available in BSD License
		// http://www.openjs.com/scripts/events/keyboard_shortcuts/index.php
		module.factory('keyboardManager', ['$window', '$timeout', function ($window, $timeout) {
			var keyboardManagerService = {};

			var defaultOpt = {
				'type': 'keydown',
				'propagate': false,
				'inputDisabled': false,
				'target': $window.document,
				'keyCode': false
			};
			// Store all keyboard combination shortcuts
			keyboardManagerService.keyboardEvent = {};
			// Add a new keyboard combination shortcut
			keyboardManagerService.bind = function (label, callback, opt) {
				var fct, elt, code, k;
				// Initialize opt object
				opt = angular.extend({}, defaultOpt, opt);
				label = label.toLowerCase();
				elt = opt.target;

				if (typeof opt.target === 'string') {
					elt = document.getElementById(opt.target);
				}

				fct = function (e) {
					e = e || $window.event;

					// Disable event handler when focus input and textarea
					if (opt['inputDisabled']) {
						var elt;
						if (e.target) {
							elt = e.target;
						}
						else if (e.srcElement) {
							elt = e.srcElement;
						}

						if (elt.nodeType === 3) {
							elt = elt.parentNode;
						}

						if (elt.tagName === 'INPUT' || elt.tagName === 'TEXTAREA') {
							return;
						}
					}

					// Find out which key is pressed
					if (e.keyCode) {
						code = e.keyCode;
					}
					else if (e.which) {
						code = e.which;
					}

					var character = String.fromCharCode(code).toLowerCase();

					if (code === 188) {
						character = ","; // If the user presses , when the type is onkeydown
					}
					if (code === 190) {
						character = "."; // If the user presses , when the type is onkeydown
					}

					var keys = label.split("+");
					// Key Pressed - counts the number of valid keypresses - if it is same as the number of keys, the shortcut function is invoked
					var kp = 0;
					// Work around for stupid Shift key bug created by using lowercase - as a result the shift+num combination was broken
					var shift_nums = {
						"`": "~",
						"1": "!",
						"2": "@",
						"3": "#",
						"4": "$",
						"5": "%",
						"6": "^",
						"7": "&",
						"8": "*",
						"9": "(",
						"0": ")",
						"-": "_",
						"=": "+",
						";": ":",
						"'": "\"",
						",": "<",
						".": ">",
						"/": "?",
						"»": "?",
						"«": "?",
						"¿": "?",
						"\\": "|"
					};
					// Special Keys - and their codes
					var special_keys = {
						'esc': 27,
						'escape': 27,
						'tab': 9,
						'space': 32,
						'return': 13,
						'enter': 13,
						'backspace': 8,

						'scrolllock': 145,
						'scroll_lock': 145,
						'scroll': 145,
						'capslock': 20,
						'caps_lock': 20,
						'caps': 20,
						'numlock': 144,
						'num_lock': 144,
						'num': 144,

						'pause': 19,
						'break': 19,

						'insert': 45,
						'home': 36,
						'delete': 46,
						'end': 35,

						'pageup': 33,
						'page_up': 33,
						'pu': 33,

						'pagedown': 34,
						'page_down': 34,
						'pd': 34,

						'left': 37,
						'up': 38,
						'right': 39,
						'down': 40,

						'f1': 112,
						'f2': 113,
						'f3': 114,
						'f4': 115,
						'f5': 116,
						'f6': 117,
						'f7': 118,
						'f8': 119,
						'f9': 120,
						'f10': 121,
						'f11': 122,
						'f12': 123
					};
					// Some modifiers key
					var modifiers = {
						shift: {
							wanted: false,
							pressed: e.shiftKey ? true : false
						},
						ctrl: {
							wanted: false,
							pressed: e.ctrlKey ? true : false
						},
						alt: {
							wanted: false,
							pressed: e.altKey ? true : false
						},
						meta: { //Meta is Mac specific
							wanted: false,
							pressed: e.metaKey ? true : false
						}
					};
					// Foreach keys in label (split on +)
					for (var i = 0, l = keys.length; k = keys[i], i < l; i++) {
						switch (k) {
							case 'ctrl':
							case 'control':
								kp++;
								modifiers.ctrl.wanted = true;
								break;
							case 'shift':
							case 'alt':
							case 'meta':
								kp++;
								modifiers[k].wanted = true;
								break;
						}

						if (k.length > 1) { // If it is a special key
							if (special_keys[k] === code) {
								kp++;
							}
						} else if (opt['keyCode']) { // If a specific key is set into the config
							if (opt['keyCode'] === code) {
								kp++;
							}
						} else { // The special keys did not match
							if (character === k) {
								kp++;
							}
							else {
								if (shift_nums[character] && e.shiftKey) { // Stupid Shift key bug created by using lowercase
									character = shift_nums[character];
									if (character === k) {
										kp++;
									}
								}
							}
						}
					}

					if (kp === keys.length &&
						modifiers.ctrl.pressed === modifiers.ctrl.wanted &&
						modifiers.shift.pressed === modifiers.shift.wanted &&
						modifiers.alt.pressed === modifiers.alt.wanted &&
						modifiers.meta.pressed === modifiers.meta.wanted) {
						$timeout(function () {
							callback(e);
						}, 1);

						if (!opt['propagate']) { // Stop the event
							// e.cancelBubble is supported by IE - this will kill the bubbling process.
							e.cancelBubble = true;
							e.returnValue = false;

							// e.stopPropagation works in Firefox.
							if (e.stopPropagation) {
								e.stopPropagation();
								e.preventDefault();
							}
							return false;
						}
					}

				};
				// Store shortcut
				keyboardManagerService.keyboardEvent[label] = {
					'callback': fct,
					'target': elt,
					'event': opt['type']
				};
				//Attach the function with the event
				if (elt.addEventListener) {
					elt.addEventListener(opt['type'], fct, false);
				}
				else if (elt.attachEvent) {
					elt.attachEvent('on' + opt['type'], fct);
				}
				else {
					elt['on' + opt['type']] = fct;
				}
			};
			// Remove the shortcut - just specify the shortcut and I will remove the binding
			keyboardManagerService.unbind = function (label) {
				label = label.toLowerCase();

				var binding = keyboardManagerService.keyboardEvent[label];
				delete(keyboardManagerService.keyboardEvent[label]);

				if (!binding) {
					return;
				}

				var type = binding['event'],
					elt = binding['target'],
					callback = binding['callback'];

				if (elt.detachEvent) {
					elt.detachEvent('on' + type, callback);
				}
				else if (elt.removeEventListener) {
					elt.removeEventListener(type, callback, false);
				}
				else {
					elt['on' + type] = false;
				}
			};
			//
			return keyboardManagerService;
		}]);

	});
